﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;


[StructLayout(LayoutKind.Explicit)]
public struct Bit128
{
    [FieldOffset(0)]
    public ulong a;
    [FieldOffset(8)]
    public ulong b;

    [FieldOffset(0)]
    public UInt32 v0;
    [FieldOffset(4)]
    public UInt32 v1;
    [FieldOffset(8)]
    public UInt32 v2;
    [FieldOffset(12)]
    public UInt32 v3;
    public void Write(ulong value, int offset, int bitCount)
    {
        if (offset < 64)
        {
            if (offset + bitCount > 64)
            {
                ulong t = value << offset;
                a |= t;
                t = value >> (64 - offset);
                b |= t;
            }
            else
            {
                value <<= offset;
                a |= value;
            }
        }
        else
        {
            offset -= 64;
            value <<= offset;
            b |= value;
        }
    }
    public uint GetMD4()
    {
        return MdFour16(v0,v1,v2,v3);
    }
    private UInt32 F(UInt32 X, UInt32 Y, UInt32 Z)
    {
        return (X & Y) | ((~X) & Z);
    }

    private UInt32 G(UInt32 X, UInt32 Y, UInt32 Z)
    {
        return (X & Y) | (X & Z) | (Y & Z);
    }

    private UInt32 H(UInt32 X, UInt32 Y, UInt32 Z)
    {
        return X ^ Y ^ Z;
    }

    private UInt32 LShift(UInt32 X, int s)
    {
        X &= 0xFFFFFFFF; return ((X << s) & 0xFFFFFFFF) | (X >> (32 - s));
    }

    private void FF(ref UInt32 X, UInt32 Y, UInt32 Z, UInt32 O, UInt32 P, int S)
    {
        X = LShift(X + F(Y, Z, O) + P, S);
    }

    private void HH(ref UInt32 X, UInt32 Y, UInt32 Z, UInt32 O, UInt32 P, int S)
    {
        X = LShift(X + G(Y, Z, O) + P + (UInt32)0x5A827999, S);
    }

    private void GG(ref UInt32 X, UInt32 Y, UInt32 Z, UInt32 O, UInt32 P, int S)
    {
        X = LShift(X + H(Y, Z, O) + P + (UInt32)0x6ED9EBA1, S);
    }
    private UInt32 MdFour16(UInt32 x0, UInt32 x1, UInt32 x2, UInt32 x3)
    {
        UInt32 AA;
        UInt32 A = 0x67452301;
        UInt32 B = 0xefcdab89;
        UInt32 C = 0x98badcfe;
        UInt32 D = 0x10325476;

        AA = A;

        FF(ref A, B, C, D, x0, 3);
        FF(ref D, A, B, C, x1, 7);
        FF(ref C, D, A, B, x2, 11);
        FF(ref B, C, D, A, x3, 19);

        HH(ref A, B, C, D, x0, 3);
        HH(ref A, B, C, D, x1, 3);
        HH(ref A, B, C, D, x2, 3);
        HH(ref A, B, C, D, x3, 3);

        GG(ref A, B, C, D, x1, 3);
        GG(ref A, B, C, D, x2, 3);
        GG(ref A, B, C, D, x1, 3);
        GG(ref A, B, C, D, x3, 3);

        A += AA;
        return A;
    }
}
